package net.scala.chapter3.test

import com.mongodb.casbah.MongoConnection
import com.typesafe.scalalogging.slf4j.LazyLogging
import net.scala.chapter3.c._
import net.scala.chapter3.p.PackageTest
import net.scala.chapter3.v.Wrapper
import org.junit.{Assert, Test}
import org.scalatest.junit.AssertionsForJUnit

/**
 * @author Barudisshu
 */
class TestScala extends AssertionsForJUnit with LazyLogging {

  /*------------
   |变量部分
   ------------*/

  var name: StringBuilder = _
  val evenNumbers = List(2, 4, 6, 8, 10)

  /*------------
   |测试部分
   ------------*/

  /**
   * 包对象测试
   */
  @Test def packageObj() {
    val p = PackageTest(Array(3, 2, 1))
    logger.info(p.inline + "\n ↳ " + p.age + "\n ↳ " + p.serveDrinks)
  }

  /**
   * MongoDB单元测试
   */
  @Test def testMongo() {
    val mongoClient = new MongoClient()
    val db = mongoClient.db("test")
    val person = db.updatableCollection("person") // 设置不同的视图
    val one = person.findOne
    logger info f"MongoDB数据库信息为: \n ↳ $one" // pointfree-style
  }

  /**
   * MongoDB Casbah单元测试
   * @see http://api.mongodb.org/scala/casbah/2.0/tutorial.html
   */
  @Test def testCasbah() {
    val mongoColl = MongoConnection()("test")("person")
    logger.info("所有数据信息如下: ")
    for (x <- mongoColl.find()) println(f" ↳ $x")
  }

  /**
   * 测试模式匹配的用法
   */
  @Test def testCaseClass() {
    // 变量匹配
    case class Person(firstName: String, lastName: String)
    val p = Person("Matt", "vanvleet") // case class会自动生成伴生对象
    p match {
      case Person(first, last) => println(" ↳ " + first + ", " + last)
    }

    // for-comprehension实现模式匹配
    val people = List(Person("Simon", "Kish"), Person("Phil", "Marzullo"), Person("Eric", "Weimer"))
    for (Person(first, last) <- people) println(first + "," + last)

    abstract class Item
    case class Book(description:String,price:Double) extends Item
    case class Bundle(description:String,price:Double,items:Item *) extends Item

    def caseClassNested(person:Item):Unit = person match {
      case Bundle(_, _,art @Book(_,_), rest @_*) => println(art.description + ": " + art.price)
      case Book(desc,price) => println(desc + ": " + price)
      case _ => Nil
    }

    caseClassNested(
      Bundle("Scala in action", 23.5, Book("Scala Cookbook", 45.5),
        Bundle("Hadoop", 12.5, Book("Hive", 89.5), Book("HBase", 32.5))
      )
    )
  }

  /**
   * 命名参数测试
   */
  @Test def testNamed() {
    trait Person {
      def grade(year: Int): String
    }
    class SalesPerson extends Person {
      override def grade(yrs: Int): String = "Senior"
    }

    val s = new SalesPerson
    println(s.grade(yrs = 1))

    val p: Person = new SalesPerson
    println(p.grade(year = 1))
  }

  /**
   * 默认参数和copy方法测试
   * copy方法常常和命名参数结合，用于重构实例的情况
   */
  @Test def testDefaultArgument() {
    val skipOption = Skip(10, NoOption)
    val skipWithLimit = skipOption.copy(anotherOption = Limit(10, NoOption))
    val skipWithNumLimit = Skip(10, Limit(10, NoOption))
    println(skipWithLimit)
    Assert.assertEquals("两者是否等价？", skipWithLimit == skipWithNumLimit, true)
  }

  /**
   * 在Scala中，override是强制性的，这意味着重构的方法体
   * 是具体的内容，如果包含抽象部分，需要声明abstract修改器。
   * 规定abstract override修改器只能用于trait中
   */
  @Test def testOverrideModify() {

    trait DogMood{def greet()}
    trait AngryMood extends DogMood{
      abstract override def greet() = {
        super.greet() // 因为父类方法greet()是抽象的，所以应该添加abstract关键字修饰
        println("bark")
      }
    }
    class Mood extends DogMood {
      override def greet() = {
        println("The Dog's mood now is: ")
      }
    }

    // 抽象依赖于具体实现，这个AngryMood的super即是Mood
    class Angry extends Mood with AngryMood

    val angry = new Angry
    angry.greet()
  }

  /**
   * 值类(value-class)
   * 由于value class继承自AnyVal，所以该类不能是本地的
   */
  @Test def testValueClass() {

    val w = new Wrapper("hey")
    w.p
  }

  /**
   * 隐式转换(implicit conversion)
   * 隐式转换只在编译期进行，如果发生转换错误，则检索查找对应的隐式转换器
   */
  @Test def testImplicitConversion(){
    implicit def double2Int(d:Double):Int = d.toInt
    val someInt:Int = 2.3
    println(someInt)

    /**
     * 实现隐式转换的两种方法
     */
/*
    // 方法一：
    class RangeMaker(left:Int){def -->(right:Int) = left to right}
    implicit def int2RangeMaker(left:Int) = new RangeMaker(left)
*/

    // 方法二:（推荐）
    implicit class RangeMaker(left:Int){def -->(right:Int) = left to right}

    /**
     * 继承AnyVal变为值类(value-class)可减少内存开销
     * 隐式转换虽然功能强大，但是会增加维护的成本
     */
//    implicit class RangeMaker(left:Int) extends AnyVal {def -->(right:Int) = left to right}
    val oneTo10 = 1 --> 10
    println(oneTo10)
  }

}
